package com.ddch.udpsocket.socket.udp;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.ddch.udpsocket.bean.Users;
import com.ddch.udpsocket.common.Config;
import com.ddch.udpsocket.listener.OnMessageReceiveListener;
import com.ddch.udpsocket.util.DeviceUtil;
import com.ddch.udpsocket.util.HeartBeatTimer;
import com.ddch.udpsocket.util.Util;
import com.ddch.udpsocket.util.WifiUtil;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.ddch.udpsocket.common.Config.POOL_SIZE;

/**
 * created by 韦敏敏
 * on 2019/12/18
 */
public class UDPSocket {
    private static final String TAG = "liaoming";
    private static final int BUFFER_LENGTH = 1024;
    private byte[] receiveByte = new byte[BUFFER_LENGTH];

    private static  String BROARDCAST_IP = "192.168.43.255";
    // 端口号，飞鸽协议默认端口2425
    public static final int CLIENT_PORT = 2425;
    private boolean isThreadRunning = false;

    private Context mContext;
    private DatagramSocket client;
    private DatagramPacket receivePacket;

    private long lastReceiveTime = 0;
    private static final long TIME_OUT = 120 * 1000;
    private static final long HEARTBEAT_MESSAGE_DURATION = 10 * 1000;

    private ExecutorService mThreadPool;
    private Thread clientThread;
    private HeartBeatTimer timer;
    private Users localUser;
    private Users remoteUser;
    private Handler mHandler;

    private final List<OnMessageReceiveListener> messageReceiveList;
    public UDPSocket(Context context){
        this.mContext = context;
        int cpuNumbers = Runtime.getRuntime().availableProcessors();
        // 根据CPU数目初始化线程池
        mThreadPool = Executors.newFixedThreadPool(cpuNumbers * POOL_SIZE);
        // 记录创建对象时的时间
        lastReceiveTime = System.currentTimeMillis();
        messageReceiveList = new ArrayList<>();
        Util.log("创建 UDP 对象");
        //        createUser();
    }

    /**
     * 创建本地用户信息
     */
    private void createUser(){
        if(localUser == null)
            localUser = new Users();
        if(remoteUser == null)
            remoteUser = new Users();
        localUser.setImei(DeviceUtil.getDeviceId(mContext));
        localUser.setSoftVersion(DeviceUtil.getPackageVersionCode(mContext));
        if(WifiUtil.getInstance(mContext).isWifiApEnabled()){ // 判断当前是否是开启热点方
            localUser.setIp("192.168.43.1");
        } else { // 当前是开启wifi方
            localUser.setIp(WifiUtil.getInstance(mContext).getLocalIPAddress());
            remoteUser.setIp(WifiUtil.getInstance(mContext).getServerIPAddress());
        }
    }
    public void startUDPSocket(Handler handler){
        this.mHandler = handler;
        if(client != null) return;
        try {
            // 表示这个Socket在设置的端口上监听数据
            client = new DatagramSocket(CLIENT_PORT);
            client.setReuseAddress(true);
            if(receivePacket == null) {
                // 创建接受数据的packet
                receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
            }
            startSocketThread();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    /**
     *  开启接收数据的线程
     */
    private void startSocketThread() {
        clientThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "clientThread is running ...");
                receiveMessage();
            }
        });
        isThreadRunning = true;
        clientThread.start();
        Util.log("开启UDP数据接收线程");
        startHeartbeatTimer();
    }

    /**
     *  启动心跳，timer 间隔十秒
     */
    public void startHeartbeatTimer() {
        if(timer == null)
            timer = new HeartBeatTimer();
        timer.setOnScheduleListener(new HeartBeatTimer.OnScheduleListener() {
            @Override
            public void onSchedule() {
                Log.d(TAG, "timer is onSchedule ...");
                long duration = System.currentTimeMillis() - lastReceiveTime;
                Log.d(TAG, "duration: " + duration);
                if(duration > TIME_OUT){ // 若超过两分钟都没有收到我的心跳包，则认为对方不在线
                    Log.d(TAG, "超时，对方已经下线");
                    // 刷新时间，重新进入下一个心跳周期
                    lastReceiveTime = System.currentTimeMillis();
                } else if(duration > HEARTBEAT_MESSAGE_DURATION){ // 若超过十秒他没有收到我的心跳包，则重新发一个
                    JSONObject jsonObject = new JSONObject();
                    try {
                        jsonObject.put(Config.MSG, Config.HEARTBREAK);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
//                    String string = "hello, this is a heartbeat message";
                    sendMessage(jsonObject.toString());
                }
            }
        });
        timer.startTimer(0, 1000 * 5);
    }

    /**
     * 发送心跳包
     * @param message
     */
    public void sendMessage(final String message) {
        mThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    BROARDCAST_IP = WifiUtil.getBroadcastAddress();
                    Util.log("BROARDCAST_IP: " + BROARDCAST_IP);
                    InetAddress targetAddress = InetAddress.getByName(BROARDCAST_IP);
                    //                    byte[] buf = message.getBytes("utf-8");
                    byte[] sendData = createSendData(message);
                    DatagramPacket packet = new DatagramPacket(sendData, sendData.length, targetAddress, CLIENT_PORT);
                    client.send(packet);
                    // 数据发送事件
                    Log.d(TAG, "数据发送成功!");
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });
    }

    private byte[] createSendData(String message) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        try {
            dos.writeUTF(message);
            dos.flush();
            dos.close();
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }

    /**
     *  处理接收到的消息
     */
    private void receiveMessage() {
        while(isThreadRunning){
            Log.d(TAG, "clientThread is running ...");
            try {
                if(client != null){
                    client.receive(receivePacket);
                }
                lastReceiveTime = System.currentTimeMillis();
                Log.d(TAG, "receive packet success ...");
            } catch (IOException e) {
                e.printStackTrace();
                Util.log("UDP数据包接收失败! 线程终止");
                stopUDPSocket();
                return;
            }
            if(receivePacket == null || receivePacket.getLength() == 0){
                Util.log("无法收到UDP数据，或者接收到的UDP数据为空");
                continue;
            }
            try {
                //                String strReceive = new String(receivePacket.getData(), 0, receivePacket.getLength());
                String receiveData = createReceiveData(receivePacket);
                String heartBeatMsg = "hello, this is a heartbeat message";
                if(!heartBeatMsg.equals(receiveData)){
                    Message receiveMsg = Message.obtain();
                    receiveMsg.what = 0;
                    receiveMsg.obj = receiveData;
                    mHandler.sendMessage(receiveMsg);
                    Log.e(TAG, "receiveMessage : " + receiveData);
                }
                // 解析接收到的json信息
                notifyMessageReceive(receiveData);
                // 每次接收完UDP数据后，重置长度,否则可能导致下次收到数据包被截断
                if(receivePacket != null){
                    receivePacket.setLength(BUFFER_LENGTH);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }


    }

    /**
     * 将消息通过接口发送到每个界面
     * @param receiveData
     */
    private void notifyMessageReceive(String receiveData) {
        for(OnMessageReceiveListener listener : messageReceiveList){
            if(listener != null){
                listener.onMessageReceived(receiveData);
            }
        }
    }

    private String createReceiveData(DatagramPacket dp) {
        DataInputStream stream  = new DataInputStream(new ByteArrayInputStream(dp.getData(), dp.getOffset(), dp.getLength()));
        try {
            String msg = stream.readUTF();
            stream.close();
            return msg;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void stopUDPSocket() {
        isThreadRunning = false;
        receivePacket = null;
        stopHeartbeatTimer();
        if(clientThread != null){
            clientThread.interrupt();
        }
        if(client != null){
            client.close();
            client = null;
        }
        if(timer != null){
            timer.exit();
        }
    }
    // 添加消息监听器
    public void addOnMessageReceiveListener(OnMessageReceiveListener listener){
        messageReceiveList.add(listener);
    }
    public void stopHeartbeatTimer(){
        if(timer != null){
            timer.exit();
            timer = null;
        }
    }
}
